﻿using FlyingEye.Consts;
using FlyingEye.Extensions;
using FlyingEye.Middlewares.RateLimiting;
using FlyingEye.Models;
using FlyingEye.Models.CorsPolicy;
using FlyingEye.Utils;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
#if DEBUG
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
#endif
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.RateLimiting;
using Volo.Abp;
using Volo.Abp.Modularity;

namespace FlyingEye
{
    [DependsOn(
        typeof(Volo.Abp.AspNetCore.Mvc.AbpAspNetCoreMvcModule),
        typeof(Volo.Abp.Autofac.AbpAutofacModule),
        typeof(Volo.Abp.AutoMapper.AbpAutoMapperModule),
        typeof(FlyingEye.ApplicationModule),
        typeof(FlyingEye.FlyingEyeCommonModule)
#if DEBUG
        , typeof(Volo.Abp.Swashbuckle.AbpSwashbuckleModule)
#endif
        )]
    public class WebApiModule : AbpModule
    {
        private string _plicyName = string.Empty;

        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();

            // 设置应用程序启动路径，一定是基于主函数所在的模块作为启动路径。
            var mainAsselbly = Assembly.GetAssembly(typeof(WebApiModule));

            if (mainAsselbly is null)
            {
                throw new System.InvalidOperationException("主模块数程序集为 NULL！");
            }

            var startPath = Path.GetDirectoryName(mainAsselbly.Location);

            if (string.IsNullOrWhiteSpace(startPath) || !Directory.Exists(startPath))
            {
                throw new InvalidOperationException("无效的启动路径！");
            }

            Configure<ApplicationStartInOptions>(options =>
            {
                options.StartPath = startPath;
            });

            var userSlidingRateLimiterOptions = configuration.GetSection(ConfigurationConsts.UserSlidingRateLimiterOptions)
                .Get<UserSlidingRateLimiterOptions>();

            var userFixedRateLimiterOptions = configuration.GetSection(ConfigurationConsts.UserFixedRateLimiterOptions)
              .Get<UserFixedRateLimiterOptions>();

            context.Services.AddRateLimiter((RateLimiterOptions rateLimiterOptions) =>
            {
                // 配置 RateLimiterOptions
                rateLimiterOptions.AddSlidingWindowLimiter(policyName: RateLimiterPolicyNames.UserSlidingPolicy, options =>
                {
                    // 可在一个窗口中提供的最大请求数
                    options.PermitLimit = userSlidingRateLimiterOptions.PermitLimit;
                    // 指定补货之间的最短期限
                    options.Window = TimeSpan.FromSeconds(userSlidingRateLimiterOptions.WindowForSeconds);
                    // 指定窗口划分到的最大段数
                    options.SegmentsPerWindow = userSlidingRateLimiterOptions.SegmentsPerWindow;
                    options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
                    // 排队的获取请求的最大累计允许计数
                    options.QueueLimit = userSlidingRateLimiterOptions.QueueLimit;
                });

                rateLimiterOptions.AddFixedWindowLimiter(policyName: RateLimiterPolicyNames.UserFixedPolicy, options =>
                {
                    options.PermitLimit = userFixedRateLimiterOptions.PermitLimit;
                    options.Window = TimeSpan.FromSeconds(userFixedRateLimiterOptions.WindowForSeconds);
                    options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
                    options.QueueLimit = userFixedRateLimiterOptions.QueueLimit;
                });
            });

            context.Services.AddHttpContextAccessor();
            context.Services.AddJwtBearerService();
            context.Services.AddControllers();

            var corsOptions = configuration.GetSection(ConfigurationConsts.CorsPolicyOptions).Get<CorsPolicyOptions>();

            if (corsOptions.Origins?.Any() == true)
            {
                _plicyName = corsOptions.PolicyName;
                context.Services.AddCors(options =>
                {
                    options.AddPolicy(corsOptions.PolicyName, options =>
                    {
                        options.WithOrigins(corsOptions.Origins.ToArray());
                    });
                });
            }
#if DEBUG
            // 如果使用 swagger，最小 api 模型，应该添加这个中间件
            // 详见：https://stackoverflow.com/questions/71932980/what-is-addendpointsapiexplorer-in-asp-net-core-6/71933535#71933535
            context.Services.AddEndpointsApiExplorer();
            context.Services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo { Title = "FlyingEyeWebApi", Version = "v1" });
                var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                options.IncludeXmlComments(Path.Combine(startPath, xmlFilename));

                // 添加JWT认证配置到Swagger
                var securityScheme = new OpenApiSecurityScheme
                {
                    Name = "Authorization",
                    Type = SecuritySchemeType.Http,
                    Scheme = "Bearer",
                    BearerFormat = "JWT"
                };
                options.AddSecurityDefinition("Bearer", securityScheme);
                options.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            }
                        },
                        new string[] { }
                    }
                });
            });
#endif
            base.ConfigureServices(context);
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            // 推荐中间件顺序。
            // ExceptionHandler
            // HSTS
            // HttpsRedirection
            // StaticFile
            // Routing
            // CORS
            // Authentication
            // Authorization

            app.UseHttpsRedirection();
            app.UseRouting();

            if (!string.IsNullOrWhiteSpace(_plicyName))
            {
                app.UseCors(_plicyName);
            }

#if DEBUG
            if (env.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI(options =>
                {
                    options.SwaggerEndpoint("/swagger/v1/swagger.json", "FlyingEyeWebApi");
                });
            }
#endif
            // 方法调用顺序不得更改。
            app.UseAuthentication(); // 使用认证。
            app.UseRateLimiter();    // 使用限速器，限速器中需要获取用户 token 里的用户 id，所以需要在认证之后使用，才能正确获取到用户 id。
            app.UseAuthorization();  // 使用授权。

            app.UseEndpoints(options =>
            {
                options.MapControllers();
            });

            base.OnApplicationInitialization(context);
        }
    }
}
